package httputil.okhttpinterceptor;

import okhttp3.*;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;

import javax.security.auth.Subject;
import javax.security.auth.kerberos.KerberosPrincipal;
import javax.security.auth.kerberos.KerberosTicket;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.Configuration;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.Principal;
import java.security.PrivilegedAction;
import java.util.Date;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class KerberosUtils {
    String host;
    Map<String,String> krbOptions;
    LoginContext loginContext;
    static final private ConnectionPool connectionPool = new ConnectionPool();
    public KerberosUtils(String host, Map<String, String> krbOptions) {
        this.host = host;
        this.krbOptions = krbOptions;


    }

    /**
     * Class to create Kerberos Configuration object which specifies the Kerberos
     * Login Module to be used for authentication.
     *
     */
    private class KerberosLoginConfiguration extends Configuration {
        Map<String,String> krbOptions = null;

        public KerberosLoginConfiguration() {}

        KerberosLoginConfiguration(Map<String,String> krbOptions) {

            this.krbOptions = krbOptions;
        }
        @Override
        public AppConfigurationEntry[] getAppConfigurationEntry(String name) {

            return new AppConfigurationEntry[] {
                    new AppConfigurationEntry(
                    "com.sun.security.auth.module.Krb5LoginModule",
                        AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
                    krbOptions)
            };
        }
    }

    private void buildSubjectCredentials() throws LoginException {

        Set<Principal> princ = new HashSet<Principal>(1);
        String keberosUser = krbOptions.get("principal");
        princ.add(new KerberosPrincipal(keberosUser));
        Subject subject = new Subject(false, princ, new HashSet<Object>(), new HashSet<Object>());
        /**
         * We are not getting the TGT from KDC here. The actual TGT is got from the
         * KDC using kinit or equivalent but we use the cached TGT in order to build
         * the LoginContext and populate the TGT inside the Subject using
         * Krb5LoginModule
         */
        LoginContext lc = new LoginContext("Krb5Login", subject, null,
                (krbOptions != null) ? new KerberosLoginConfiguration(krbOptions) : new KerberosLoginConfiguration());
        lc.login();
        loginContext = lc;
    }
//    private class requestAction  implements PrivilegedAction<Future<String>>
//    {
//        OkHttpClient client;
//        Request request;
//
//        public requestAction(OkHttpClient client,Request request){
//            this.client = client;
//            this.request = request;
//        }
//
//        @Override
//        public Future<String>  run() {
//            CompletableFuture<String > cf = new CompletableFuture<>();
//
//            final Call call = client.newCall(request);
//
//            call.enqueue(new Callback() {
//                @Override
//                public void onFailure(Call call, IOException e) {
//                    e.printStackTrace();
//                    cf.complete("Failure");
//                }
//
//                @Override
//                public void onResponse(Call call, Response response) throws IOException {
//                    InputStream is = response.body().byteStream();
//                    System.out.println("Status code " + response.code());
//                    //System.out.println("message is :"+ Arrays.deepToString(response.headers()));
//                    StringBuilder fullpath = new StringBuilder();
//                    String fileName = String.valueOf(System.currentTimeMillis());
//                    String dir = KerberosUtils.class.getClassLoader().getResource("").getPath();
//                    fullpath.append(dir );
//                    fullpath.append(fileName);
//                    fullpath.append("applications");
//                    fullpath.append(".json");
//                    FileOutputStream fos = new FileOutputStream( fullpath.toString() );
//
//                    fos.write(IOUtils.toByteArray(is) );
//                    fos.close();
//                    cf.complete("Success");
//
//                }
//            });
//            return cf;
//        }
//    }
    public void executeRequest( Request request)throws LoginException{
        /*
         * Check if the TGT in the Subject's private credentials are valid. If
         * valid, then we use the TGT in the Subject's private credentials. If not,
         * we build the Subject's private credentials again from valid TGT in the
         * Kerberos client cache.
         */
        buildSubjectCredentials();

        Set<Object> privateCreds = loginContext.getSubject().getPrivateCredentials();
        for (Object privateCred : privateCreds) {
            if (privateCred instanceof KerberosTicket) {
                String serverPrincipalTicketName = ((KerberosTicket) privateCred).getServer().getName();
                if ((serverPrincipalTicketName.startsWith("krbtgt"))
                        && ((KerberosTicket) privateCred).getEndTime().compareTo(new Date()) == -1) {
                    buildSubjectCredentials();
                    break;
                }
            }
        }

        Subject.doAs(loginContext.getSubject(),new PrivilegedAction<String>() {


            @Override
            public String  run() {

                OkHttpClient.Builder clientBldr = new OkHttpClient.Builder()
                        //.followRedirects(false)
                        //.followSslRedirects(false)
                        // all clients share a single connection pool
                        .connectionPool(connectionPool)
                        // cookies are ignored (except when a Transaction is being used)
                        //.cookieJar(CookieJar.NO_COOKIES)
                        // no timeouts since some of our clients' reads and writes can be massive
                        .readTimeout(0, TimeUnit.SECONDS)
                        .writeTimeout(0, TimeUnit.SECONDS);
                Interceptor interceptor = new HTTPKerberosAuthInterceptor(host );


                if(interceptor != null)
                    clientBldr.addInterceptor(interceptor);

                OkHttpClient  client = clientBldr.build();
                String result = "Failure";
                CompletableFuture<String > cf = new CompletableFuture<>();



                try (Response response = client.newCall(request).execute()) {
                    System.out.println("Status code " + response.code());
                        InputStream is = response.body().byteStream();
                        System.out.println("Status code " + response.code());
                        //System.out.println("message is :"+ Arrays.deepToString(response.headers()));
                        StringBuilder fullpath = new StringBuilder();
                        String fileName = String.valueOf(System.currentTimeMillis());
                        String dir = KerberosUtils.class.getClassLoader().getResource("").getPath();
                        fullpath.append(dir );
                        fullpath.append(fileName);
                        fullpath.append("applications");
                        fullpath.append(".json");
                        FileOutputStream fos = new FileOutputStream( fullpath.toString() );

                        fos.write(IOUtils.toByteArray(is) );
                        fos.close();
                    return response.body().string();
                }
                catch (IOException ioe){
                    ioe.printStackTrace();
                }
//                call.enqueue(new Callback() {
//                    @Override
//                    public void onFailure(Call call, IOException e) {
//                        e.printStackTrace();
//                        cf.complete("Failure");
//                    }
//
//                    @Override
//                    public void onResponse(Call call, Response response) throws IOException {
//                        InputStream is = response.body().byteStream();
//                        System.out.println("Status code " + response.code());
//                        //System.out.println("message is :"+ Arrays.deepToString(response.headers()));
//                        StringBuilder fullpath = new StringBuilder();
//                        String fileName = String.valueOf(System.currentTimeMillis());
//                        String dir = KerberosUtils.class.getClassLoader().getResource("").getPath();
//                        fullpath.append(dir );
//                        fullpath.append(fileName);
//                        fullpath.append("applications");
//                        fullpath.append(".json");
//                        FileOutputStream fos = new FileOutputStream( fullpath.toString() );
//
//                        fos.write(IOUtils.toByteArray(is) );
//                        fos.close();
//                        cf.complete("Success");
//
//                    }
//                });
                try {
                    result = cf.get();
                }catch(Exception e){
                    result = "exception";
                }
                return result;
            }
        }
        );
    }
}
